// Copyright (C) 2010 Félix Cloutier.

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License 2.0 for more details.

// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/

#ifndef LLVM_JIT_RECOMPILER
#define LLVM_JIT_RECOMPILER

#include <llvm/LLVMContext.h>
#include <llvm/Module.h>
#include <llvm/PassManager.h>
#include <llvm/Support/IRBuilder.h>

#include "JitFunctionContext.h"
#include "JitBindings.h"

#include <map>
#include "../Gekko.h"

namespace LLVMJit
{

// The recompiler class.

// The LLVM JIT is eager to recompile. As such, when it is asked to recompile
//	an address, it will recompile at once as much as it can, trying to split the
//	code into functions.

// To simplify our task to a great extent, we use coroutines to go back to the
//	emulator's "real code".

// In order to hand back control to the emulator, the JIT has to insert calls to
//	Coroutine::Yield at strategic points. In the other JIT, control is handed
//	back (if I understand correctly) after each jump; this is possibly a sad
//	bottleneck if the PPC code has many calls to small functions. The LLVM JIT
//	tries to avoid that by counting how many cycles passed at the end of each
//	function. Whenever that count reaches a certain treshold, control is handed
//	back.

// The functions generated by the LLVMJitRecompiler are still in LLVM form. The
//	JitExecutor is responsible for turning that into machine code.

// Functions generated expect `JitExecutor*`, a `Coroutine*` and a
//	`PowerPCState*`. (Relying on global objects is not something I like.) This
//	should be enough, with what lives in the `Memory` namespace, to work out any
//	PPC code.

// When GetFunction is called, first, it checks if the function was not already
//	compiled. If so, it gladfully returns the cached function. Otherwise, it
//	creates a new `llvm::Function*` to fill, and uses a `PPCAnalyst` to
//	disassemble the machine code. The first step is to identify all labels:
//	labels are the places you can reach with a direct branch. Once identified,
//	`llvm::Block*`s are created for them, and are ready to be filled with the
//	result of the instruction recompiling methods.

// Methods that recompile a single instruction are only expected to use the
//	`llvm::IRBuilder<>` member `builder`. Their task is simply to brainlessly
//	recompile the instruction: LLVM will be able to optimize that quite a bit,
//	so even if we have garbage-quality recompiled code, we should end up with
//	something decently fast (or at least, very possibly faster than what the
//	current JIT can do).

// Once a function is recompiled, we run the FunctionPassManager on it. This
//	should tidy up and clean the code. Then, we're ready to call it.

class JitRecompiler
{
	// LLVM
	llvm::LLVMContext& context;
	llvm::Module* module;
	llvm::FunctionPassManager* passManager;
	
	// Cache
	std::map<u32, llvm::Function*> functionCache;
	
	// Bindings (hopefully temporary; see JitBindings.h)
	// Maybe eventually we'll just move them to JitFunctionContext too.
	JitBindings* bindings;
	
	// Instruction map
	// (related methods implemented in JitRecompilerInstructionMap.cpp)
	typedef void (JitRecompiler::*InstructionRecompiler)(UGeckoInstruction, JitFunctionContext&);
	std::map<u32, InstructionRecompiler> dispatchTable;
	std::map<u32, InstructionRecompiler> dispatchTable4;
	std::map<u32, InstructionRecompiler> dispatchTable19;
	std::map<u32, InstructionRecompiler> dispatchTable31;
	std::map<u32, InstructionRecompiler> dispatchTable59;
	std::map<u32, InstructionRecompiler> dispatchTable63;
	void RemapInstructions();
	
	llvm::Function* RecompileFunction(u32 address);
	void EmitILForInstruction(UGeckoInstruction instruction, JitFunctionContext& context);

public:

	JitRecompiler(llvm::Module* module, const llvm::TargetData* targetData);
	
	void SetBindings(JitBindings* bindings);
	
	void ClearCache();
	void ClearCache(u32 address);
	
	llvm::Module* GetModule();
	llvm::Function* GetFunction(u32 address);
	
	// The JitRecompiler will NOT take care of releasing the memory associated
	//	to the returned Function: you must delete it yourself
	llvm::Function* GetFunction(UGeckoInstruction singleInstruction);
	
	// Will return NULL if the function was not yet compiled.
	llvm::Function* GetCachedFunction(u32 address);
	
	~JitRecompiler();

#pragma mark -

private:
#pragma mark Instructions
// This is the place we have the declarations for all instructions. The list was
//	borrowed from Interpreter.h.

// Calling any of these generates code using the llvm::IRBuilder. Each method
//	for generating code has to follow a couple of conventions to ensure the
//	whole system works properly. It's fairly simple: all recompiling methods
//	should emit all the code necessary to fully emulate the instruction, should
//	not go out of their way to optimize the code and should increment the cycles
//	counter by the correct amount of cycles that the instruction requires
//	(through JitFunctionContext).

// We have LLVM for optimizing. If we notice a pattern that it doesn't already
//	optimize, we'll simply write an optimization pass for it. Code generating
//	isn't the right place to implement optimizations.

// There seem to be instructions with special handling and names that don't look
//	like instruction names (notably the RunTable things), which I've omitted
//	here. Might have been a mistake.

#define INST(A)		void A(UGeckoInstruction inst, JitFunctionContext& context)

// TODO missing HLEFunction and DynaBlock

// Implemented in JitRecompilerInstructionMap.cpp
#pragma mark Opcode tables
	INST(DispatchTable4);
	INST(DispatchTable19);
	INST(DispatchTable31);
	INST(DispatchTable59);
	INST(DispatchTable63);

// Implemented in JitRecompilerBranch.cpp
#pragma mark Branch instructions
	INST(bx);
	INST(bcx);
	INST(bcctrx);
	INST(bclrx);

// Implemented in JitRecompilerSystem.cpp
#pragma mark Syscall
	INST(sc);

// Implemented in JitRecompilerFloatingPoint.cpp
#pragma mark Floating point instructions
	INST(faddsx);
	INST(fdivsx);
	INST(fmaddsx);
	INST(fmsubsx);
	INST(fmulsx);
	INST(fnmaddsx);
	INST(fnmsubsx);
	INST(fresx);
//	INST(fsqrtsx);	<-- was commented out in Interpreter.h
	INST(fsubsx);
	INST(fabsx);
	INST(fcmpo);
	INST(fcmpu);
	INST(fctiwx);
	INST(fctiwzx);
	INST(fmrx);
	INST(fnabsx);
	INST(fnegx);
	INST(frspx);
	INST(faddx);
	INST(fdivx);
	INST(fmaddx);
	INST(fmsubx);
	INST(fmulx);
	INST(fnmaddx);
	INST(fnmsubx);
	INST(frsqrtex);
	INST(fselx);
	INST(fsqrtx);
	INST(fsubx);

// Implemented in JitRecompilerInteger.cpp
#pragma mark Integer instructions
	INST(addi);
	INST(addic);
	INST(addic_rc);
	INST(addis);
	INST(andi_rc);
	INST(andis_rc);
	INST(cmpi);
	INST(cmpli);
	INST(mulli);
	INST(ori);
	INST(oris);
	INST(subfic);
	INST(twi);
	INST(xori);
	INST(xoris);
	INST(rlwimix);
	INST(rlwinmx);
	INST(rlwnmx);
	INST(andx);
	INST(andcx);
	INST(cmp);
	INST(cmpl);
	INST(cntlzwx);
	INST(eqvx);
	INST(extsbx);
	INST(extshx);
	INST(nandx);
	INST(norx);
	INST(orx);
	INST(orcx);
	INST(slwx);
	INST(srawx);
	INST(srawix);
	INST(srwx);
	INST(tw);
	INST(xorx);
	INST(addx);
	INST(addcx);
	INST(addex);
	INST(addmex);
	INST(addzex);
	INST(divwx);
	INST(divwux);
	INST(mulhwx);
	INST(mulhwux);
	INST(mullwx);
	INST(negx);
	INST(subfx);
	INST(subfcx);
	INST(subfex);
	INST(subfmex);
	INST(subfzex);

// Implemented in JitRecompilerLoadStore.cpp
#pragma mark Load/store instructions
	INST(lbz);
	INST(lbzu);
	INST(lfd);
	INST(lfdu);
	INST(lfs);
	INST(lfsu);
	INST(lha);
	INST(lhau);
	INST(lhz);
	INST(lhzu);
	INST(lmw);
	INST(lwz);
	INST(lwzu);
	INST(stb);
	INST(stbu);
	INST(stfd);
	INST(stfdu);
	INST(stfs);
	INST(stfsu);
	INST(sth);
	INST(sthu);
	INST(stmw);
	INST(stw);
	INST(stwu);
	INST(dcba);
	INST(dcbf);
	INST(dcbi);
	INST(dcbst);
	INST(dcbt);
	INST(dcbtst);
	INST(dcbz);
	INST(eciwx);
	INST(ecowx);
	INST(eieio);
	INST(icbi);
	INST(lbzux);
	INST(lbzx);
	INST(lfdux);
	INST(lfdx);
	INST(lfsux);
	INST(lfsx);
	INST(lhaux);
	INST(lhax);
	INST(lhbrx);
	INST(lhzux);
	INST(lhzx);
	INST(lswi);
	INST(lswx);
	INST(lwarx);
	INST(lwbrx);
	INST(lwzux);
	INST(lwzx);
	INST(stbux);
	INST(stbx);
	INST(stfdux);
	INST(stfdx);
	INST(stfiwx);
	INST(stfsux);
	INST(stfsx);
	INST(sthbrx);
	INST(sthux);
	INST(sthx);
	INST(stswi);
	INST(stswx);
	INST(stwbrx);
	INST(stwcxd);
	INST(stwux);
	INST(stwx);
	INST(sync);
	INST(tlbia);
	INST(tlbie);
	INST(tlbsync);

// Implemented in JitRecompilerPaired.cpp
#pragma mark Paired instructions
	INST(psq_l);
	INST(psq_lu);
	INST(psq_st);
	INST(psq_stu);
	INST(psq_lx);
	INST(psq_stx);
	INST(psq_lux);
	INST(psq_stux);
	INST(ps_div);
	INST(ps_sub);
	INST(ps_add);
	INST(ps_sel);
	INST(ps_res);
	INST(ps_mul);
	INST(ps_rsqrte);
	INST(ps_msub);
	INST(ps_madd);
	INST(ps_nmsub);
	INST(ps_nmadd);
	INST(ps_neg);
	INST(ps_mr);
	INST(ps_nabs);
	INST(ps_abs);
	INST(ps_sum0);
	INST(ps_sum1);
	INST(ps_muls0);
	INST(ps_muls1);
	INST(ps_madds0);
	INST(ps_madds1);
	INST(ps_cmpu0);
	INST(ps_cmpo0);
	INST(ps_cmpu1);
	INST(ps_cmpo1);
	INST(ps_merge00);
	INST(ps_merge01);
	INST(ps_merge10);
	INST(ps_merge11);
	INST(dcbz_l);

// Implemented in JitRecompilerSystem.cpp
#pragma mark System register instructions // (supervisor mode?)
	INST(mcrfs);
	INST(mffsx);
	INST(mtfsb0x);
	INST(mtfsb1x);
	INST(mtfsfix);
	INST(mtfsfx);
	INST(mcrxr);
	INST(mfcr);
	INST(mfmsr);
	INST(mfsr);
	INST(mfsrin);
	INST(mtmsr);
	INST(mtsr);
	INST(mtsrin);
	INST(mfspr);
	INST(mftb);
	INST(mtcrf);
	INST(mtspr);
	INST(crand);
	INST(crandc);
	INST(creqv);
	INST(crnand);
	INST(crnor);
	INST(cror);
	INST(crorc);
	INST(crxor);
	INST(mcrf);
	INST(rfi);
	INST(rfid);
//	INST(sync); 	<-- was commented out in Interpreter.h
	INST(isync);

#undef INST
	void Default(UGeckoInstruction instruction, JitFunctionContext& context);

// LLVM Helper functions
// Implemented in JitRecompilerHelpers.cpp
// Template implementations following in this file
public:
	static llvm::FunctionType* CreateFunctionType(const llvm::Type* returnType, ...);

private:
	llvm::Value* GetPPCStatePointer(llvm::IRBuilder<>& builder);
	llvm::Value* GetGprPointer(llvm::IRBuilder<>& builder, int reg);
	llvm::Value* GetSprPointer(llvm::IRBuilder<>& builder, int reg);
	llvm::Value* GetCrPointer(llvm::IRBuilder<>& builder);
	llvm::Value* GetCrFastPointer(llvm::IRBuilder<>& builder, int reg);
	llvm::Value* GetMsrPointer(llvm::IRBuilder<>& builder);
	llvm::Value* GetPsPointer(llvm::IRBuilder<>& builder, int reg);
	llvm::Value* GetFpscrPointer(llvm::IRBuilder<>& builder);
	llvm::Value* GetLinkPointer(llvm::IRBuilder<>& builder);
	llvm::Value* GetCarryPointer(llvm::IRBuilder<>& builder);
	llvm::Value* GetCtrPointer(llvm::IRBuilder<>& builder);
	llvm::Value* GetGqrPointer(llvm::IRBuilder<>& builder, int reg);
	llvm::Value* GetSrrPointer(llvm::IRBuilder<>& builder, int reg);
	llvm::Value* GetRegisterPointer(llvm::IRBuilder<>& builder, int reg);
	llvm::Value* GetRegisterPointer(llvm::IRBuilder<>& builder, int reg, int index);

	llvm::Value* LoadGReg(llvm::IRBuilder<>& builder, int reg);
	llvm::Value* LoadFReg(llvm::IRBuilder<>& builder, int reg);
	llvm::Value* LoadLink(llvm::IRBuilder<>& builder);
	llvm::Value* LoadCR(llvm::IRBuilder<>& builder, int reg);
	llvm::Value* LoadCarry(llvm::IRBuilder<>& builder);
	llvm::Value* LoadCTR(llvm::IRBuilder<>& builder);
	llvm::Value* LoadMSR(llvm::IRBuilder<>& builder);
	llvm::Value* LoadGQR(llvm::IRBuilder<>& builder, int reg);

	llvm::Value* StoreGReg(llvm::IRBuilder<>& builder, int reg, llvm::Value* value);
	llvm::Value* StoreFReg(llvm::IRBuilder<>& builder, int reg, llvm::Value* value);
	llvm::Value* StoreLink(llvm::IRBuilder<>& builder, llvm::Value* value);
	llvm::Value* StoreCR(llvm::IRBuilder<>& builder, int reg, llvm::Value* value);
	llvm::Value* StoreCarry(llvm::IRBuilder<>& builder, llvm::Value* value);
	llvm::Value* StoreCTR(llvm::IRBuilder<>& builder, llvm::Value* value);
	llvm::Value* StoreMSR(llvm::IRBuilder<>& builder, llvm::Value* value);
	llvm::Value* StoreGQR(llvm::IRBuilder<>& builder, int reg, llvm::Value* value);
	llvm::Value* StoreFPRF(llvm::IRBuilder<>& builder, llvm::Value* value);
	llvm::Value* StoreSRR(llvm::IRBuilder<>& builder, int reg, llvm::Value* value);
};

}

#endif